package dat255.calendarclock.model.event;

import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import android.util.Log;

/**
 * Handles all events where all views can get the data needed.
 * 
 * @author Marcus Hultman, Oskar Montin & Gabriel Andersson
 * 
 */
public class EventHandler extends Observable implements Observer {

	// Field
	private static EventHandler singletonInstance = null;
	private LinkedList<Event> events = new LinkedList<Event>();

	private Category customCategory = null;
	private GregorianCalendar selectedDay = new GregorianCalendar();
	private SubEvent selectedEvent = null;

	// Constructor
	private EventHandler() {
		MinuteTimer.getInstance().addObserver(this);
	}
	public static EventHandler getInstance() {

		if (singletonInstance == null) {
			singletonInstance = new EventHandler();

		}
		return singletonInstance;

	}

	// Public methods
	/**
	 * @return a set of all the categories that are available
	 */
	public Set<Category> getAllCategories() {
		Set<Category> set = new HashSet<Category>();
		for (Event e : events) {
			set.add(e.getCategory());
		}
		return set;
	}

	/**
	 * @param subEvents
	 *            the list of the subEvents that are relevant
	 * @return a set of categories that are relevant
	 */
	public Set<Category> getCategories(LinkedList<SubEvent> subEvents) {
		Set<Category> set = new HashSet<Category>();
		for (SubEvent sE : subEvents)
			set.add(sE.getEvent().getCategory());
		return set;
	}
	
	public Category getCustomCategory() {
		return customCategory;
	}

	public void setCustomCategory(Category customCategory) {
		this.customCategory = customCategory;
	}

	/**
	 * @return All events in the calendar.
	 */
	public LinkedList<Event> getEvents() {
		return this.events;
	}

	/**
	 * 
	 * @param eventIndex
	 * @return The event specified by the provided index
	 */
	public Event getEvent(int index) {
		return events.get(index);
	}

	/**
	 * Returns all events with the common precision (day of month, weekday, year
	 * etc.) as today.
	 * 
	 * @param Use
	 *            GregorianCalendar constants to define the precision.
	 * @return
	 */
	public LinkedList<SubEvent> getEvents(final int precision) {
		return getEvents(new GregorianCalendar(), precision);
	}

	/**
	 * Returns all events with the common precision (day of month, weekday, year
	 * etc.) as the specified date.
	 * 
	 * @param date
	 * @param precision
	 *            Use GregorianCalendar constants to define the precision.
	 * @return
	 */
	public LinkedList<SubEvent> getEvents(final GregorianCalendar date,
			final int precision) {

		LinkedList<SubEvent> listOfEvents = new LinkedList<SubEvent>();

		// Set start value to the start of the precision interval
		GregorianCalendar startDate = DateHelper.reset(
				((GregorianCalendar) date.clone()), precision);
		GregorianCalendar endDate = (GregorianCalendar) startDate.clone();
		// Set end value to the end of the precision interval
		endDate.add(precision, 1);
		for (Event event : events) {
			listOfEvents.addAll(SubEvent.getEvents(event, startDate, endDate));
		}
		Collections.sort(listOfEvents);
		return listOfEvents;
	}

	/**
	 * 
	 * @return the selected day
	 */
	public GregorianCalendar getSelectedDay() {
		return selectedDay;
	}

	/**
	 * Selects a day
	 * 
	 * @param selectedDay
	 */
	public void setSelectedDay(GregorianCalendar selectedDay) {
		this.selectedDay = selectedDay;
		setChanged();
		notifyObservers();
	}

	/**
	 * 
	 * @return the selected subevent
	 */
	public SubEvent getSelectedEvent() {
		return selectedEvent;
	}

	/**
	 * Selects an subevent
	 * 
	 * @param selectedEvent
	 */
	public void setSelectedEvent(SubEvent selectedEvent) {
		this.selectedEvent = selectedEvent;
	}

	/**
	 * Adds a new Event.
	 * 
	 * @param event
	 * @return
	 */
	public boolean addEvent(final Event event) {
		return events.add(event);
	}

	/**
	 * Removes an Event.
	 * 
	 * @param event
	 * @return
	 */
	public boolean removeEvent(final Event event) {
		return events.remove(event);
	}

	/**
	 * Wipe out the whole calendar. Kaboom!
	 */
	public void clearEvents() {
		events.clear();
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		GregorianCalendar now = new GregorianCalendar();
		for (Event event : events) {
			SubEvent subEvent = SubEvent.create(event, now);
			if (subEvent != null && DateHelper.gregoriansAreEqual(now, subEvent.getDate())) {
				setChanged();
				notifyObservers(event);
				event.setHasHappened(now);				
			}
		}

	}
	
	

}
